#pragma once

#include <unistd.h>
#include <vector>
#include <string>
#include <unordered_map>
#include <functional>
#include <cassert>
#include <cerrno>
#include <sys/epoll.h>
#include <poll.h>

#include "sock.hpp"
#include "epoll.hpp"
#include "Util.hpp"
#include "Prototol.hpp"

#define DEFAULTPORT 8080
#define DEFAULTCONNECTION -1
#define BUFFERSIZE 1024

namespace ServerFile
{
     static int nums = 64;
     class Connection;
     class Server;

     using fun_t = std::function<void(Connection *)>;
     using server_t = std::function<void(const std::string &message)>;

     class Connection
     {
     public:
          Connection(int socket = DEFAULTCONNECTION, Server *Sercon = nullptr)
              : _socket(socket), _Sercon(Sercon)
          {
          }

          ~Connection()
          {
          }

     public:
          void closeSocket()
          {
               if (_socket > 0)
                    close(_socket);
          }
          void registerFun(fun_t recver, fun_t sender, fun_t exceper)
          {
               _recver = recver;
               _sender = sender;
               _exceper = exceper;
          }

     public: // 这边先直接用默认构造函数
          int _socket;
          std::string _readbuffer;
          std::string _writebuffer;

          fun_t _recver;
          fun_t _sender; // 设置注册回调函数，传入指针以操作readbuffer，和writebuffer
          fun_t _exceper;

          Server *_Sercon; // Con的回指指针

          uint64_t lasttime;
     };

     // 表示就绪的事件
     class Recver
     {
     public:
          int _socket;
          uint32_t events;
     };

     class Server
     {
     private:
          void Reaver(Connection *conn)
          {
               for (;;)
               {
                    char buffer[BUFFERSIZE];
                    ssize_t s = recv(conn->_socket, buffer, sizeof buffer - 1, 0);
                    if (s > 0)
                    {
                         // 今天我就不换行了，老师这里换行了
                         buffer[s] = 0;
                         conn->_readbuffer += buffer;

                         _server(conn);
                    }
                    else if (s == 0) // 表示对方把写端关闭了
                    {
                         if (conn->_exceper != nullptr)
                         {
                              conn->_exceper(conn);

                              std::cout << "由96进入exceper" << std::endl;
                              return;
                         }
                    }
                    else
                    {
                         if (errno == EAGAIN || errno == EWOULDBLOCK)
                         {
                              logMessage(DEBUG, "recv succes exit");
                              break;
                         }
                         else if (errno == EINTR)
                         {
                              continue;
                         }
                         else
                         {
                              if (conn->_exceper != nullptr)
                              {
                                   std::cout << "由115进入exceper" << std::endl;
                                   conn->_exceper(conn);
                                   return;
                              }
                         }
                    }
               }
          }

          void Sender(Connection *conn)
          {
               while (true)
               {
                    ssize_t s = send(conn->_socket, conn->_writebuffer.c_str(), conn->_writebuffer.size(), 0);
                    if (s > 0)
                    {
                         if (conn->_writebuffer.empty())
                         {
                              enableReadWrite(conn, true, false);
                              break;
                         }
                         else
                              conn->_writebuffer.erase(0, s);
                    }
                    else if(s == 0)
                    {
                         logMessage(DEBUG,"The server information of sendto is empty");
                         break;
                    }
                    else
                    {
                         // 发送条件不满足
                         if (errno == EAGAIN || errno == EWOULDBLOCK)
                         {
                              break;
                         }
                         else if(errno == EINTR)
                         {
                              continue;
                         }
                         else
                         {
                              if (conn->_exceper)
                              {
                                   std::cout << "由150进入exceper" << std::endl;
                                   conn->_exceper(conn);
                                   return;
                              }
                         }
                    }
               }

               // if (!conn->_writebuffer.empty())
               //      conn->_Sercon->enableReadWrite(conn, true, true);
               // else
               //      conn->_Sercon->enableReadWrite(conn, true, false);
          }

          // 操作epoll模型的红黑树
          void Exceper(Connection *conn)
          {
               logMessage(DEBUG, "Exceper begin");
               _epoll.conTrol(conn->_socket, 0, EPOLL_CTL_DEL);
               _connectHash.erase(conn->_socket);

               logMessage(DEBUG, "succee erase abnormal socket %d", conn->_socket);
               conn->closeSocket();
               delete conn;
               logMessage(DEBUG, "Exceper end");
          }

          void accepter(Connection *conn)
          {
               for (;;)
               {
                    std::string clinetIP = "";
                    uint16_t clinetPort = 0;

                    int err = 0;
                    int socket = _sock.Accpet(&err);

                    if (socket > 0)
                    {
                         clinetIP = _sock._clinetIP;
                         clinetPort = _sock._clinetPort;

                         addConnection(socket, EPOLLIN | EPOLLET,
                                       std::bind(&Server::Reaver, this, std::placeholders::_1),
                                       std::bind(&Server::Sender, this, std::placeholders::_1),
                                       std::bind(&Server::Exceper, this, std::placeholders::_1));

                         logMessage(DEBUG, "get a new link ,info[%s,%d]", clinetIP.c_str(), clinetPort);
                    }
                    else if (socket < 0)
                    {
                         if (err == EAGAIN || err == EWOULDBLOCK)
                         {
                              logMessage(DEBUG, "Accept success exit");
                              break;
                         }
                         else if (err == EINTR)
                         {
                              continue;
                         }
                         else
                         {
                              logMessage(WARNING, "The listening socket %d did not receive the link", _sock.getListenSocket());

                              logMessage(WARNING, "Warning Accept exit fail");
                              break;
                         }
                    }
               }
          }

          void addConnection(int sock, uint32_t event, fun_t recver, fun_t sender, fun_t exceper)
          {
               // 1.创建connect类，然后insert进入 hash中，（先组织后描述）
               if (event & EPOLLET)
                    Util::setNonBlockSocket(_sock.getListenSocket());
               Connection *Ce = new Connection(sock, this);
               Ce->registerFun(recver, sender, exceper);

               _connectHash.insert(std::make_pair(sock, Ce));

               // 2. 将关心的事件加入epoll模型红黑树中
               bool ret = _epoll.ctrlAdd(sock, event);
               assert(ret);
               (void)ret;
          }


          bool isConnectionExist(int socket)
          {
               auto pos = _connectHash.find(socket);

               return !(pos == _connectHash.end());
          }

          void loop(int timeout)
          {
               int n = _epoll.wait(_ev, _num, timeout);
               for (int i = 0; i < n; i++)
               {
                    int socket = _ev[i].data.fd;
                    uint32_t event = _ev[i].events;

                    // 将event的异常事件转为 读写事件；  因为异常就在读写中
                    if (event & EPOLLHUP)
                         event |= (EPOLLIN | EPOLLOUT);

                    if (event & EPOLLERR)
                         event |= (EPOLLIN | EPOLLOUT);

                    // if (event & EPOLLHUP || event & EPOLLERR)
                    //      event |= (EPOLLIN | EPOLLOUT);

                    if (isConnectionExist(socket) && (event & EPOLLIN))
                    {
                         if (_connectHash.find(socket)->second->_recver)
                              _connectHash.find(socket)->second->_recver(_connectHash.find(socket)->second);
                    }

                    if (isConnectionExist(socket) && (event & EPOLLOUT))
                    {

                         if (_connectHash.find(socket)->second->_sender)
                              _connectHash.find(socket)->second->_sender(_connectHash.find(socket)->second);
                    }
                    // 所以我们就合并了
                    // 这里千万不能写  event == EPOLLIN , 一写就歇菜
                    //  if( ( event & EPOLLIN ) && socket == _sock.getListenSocket()  )
                    //  _connectHash.find(socket) ->second ->_recver(_connectHash.find(socket)->second);
                    //  else if( ( event & EPOLLIN ) && socket != _sock.getListenSocket())
                    //  _connectHash.find(socket) ->second ->_recver(_connectHash.find(socket)->second);
                    // 我就问题你这个 回调方法设置的妙不妙
               }
          }

     public:
          Server(fun_t func, uint16_t port = DEFAULTPORT)
              : _server(func), _port(port), _sock(), _epoll(), _ev(nullptr)
          {
               _connectHash.clear();
          }

          void init()
          {
               _sock.setSocket();
               _sock.Bind(_port);
               _sock.Listen();
               // accept就要用到多路转接的策略了

               // 下面开始构建epoll,把已经设置阻塞的setNonBaock，listensock套接字加入epoll
               _epoll.create();

               addConnection(_sock.getListenSocket(), EPOLLIN | EPOLLET,
                             std::bind(&Server::accepter, this, std::placeholders::_1), nullptr, nullptr);

               _ev = new struct epoll_event[nums];
               _num = nums;
          }

          void enableReadWrite(Connection *conn, bool readable, bool writeable)
          {
               uint32_t event = (readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0) | EPOLLET;
               _epoll.conTrol(conn->_socket, event, EPOLL_CTL_MOD);
          }

          // 任务派发
          void disPatch()
          {
               for (;;)
               {
                    loop(1000);
               }
          }

          ~Server()
          {
               if (_ev != nullptr)
                    delete[] _ev;
          }

     private:
          uint16_t _port;
          Sock _sock;
          Epoll _epoll;
          struct epoll_event *_ev;
          int _num;

          std::unordered_map<int, Connection *> _connectHash;
          // server_t _handler;
          fun_t _server;
     };
}